require "lfs"
require("json")
local bit = require"bit"
local io = require("io")

local importCache, nativeCache= {}, {}

function io.exists(path)
    local file = io.open(path, "r")
    if file then
        io.close(file)
        return true
    end
    return false
end

function make_directory_recursive(path)
    local system_command
    if package.config:sub(1,1) == '\\' then -- Windows
        system_command = 'if not exist "' .. path .. '" mkdir "' .. path .. '"'
    else -- Unix-like
        system_command = 'mkdir -p ' .. path
    end
    os.execute(system_command)
end

function isDirectoryExists(path)
    local attribs = lfs.attributes(path)
    if attribs and attribs.mode == "directory" then
        return true
    else
        return false
    end
end

function io.readfile(path)
    local file = io.open(path, "r")
    if file then
        local content = file:read("*a")
        io.close(file)
        return content
    end
    return nil
end

function io.writefile(path, content, mode)
    mode = mode or "w+b"
    local file = io.open(path, mode)
    if file then
        if file:write(content) == nil then return false end
        io.close(file)
        return true
    else
        return false
    end
end

function io.pathinfo(path)
    local pos = string.len(path)
    local extpos = pos + 1
    while pos > 0 do
        local b = string.byte(path, pos)
        if b == 46 then -- 46 = char "."
            extpos = pos
        elseif b == 47 then -- 47 = char "/"
            break
        end
        pos = pos - 1
    end

    local dirname = string.sub(path, 1, pos)
    local filename = string.sub(path, pos + 1)
    extpos = extpos - pos
    local basename = string.sub(filename, 1, extpos - 1)
    local extname = string.sub(filename, extpos)
    return {
        dirname = dirname,
        filename = filename,
        basename = basename,
        extname = extname
    }
end

function io.filesize(path)
    local size = false
    local file = io.open(path, "r")
    if file then
        local current = file:seek()
        size = file:seek("end")
        file:seek("set", current)
        io.close(file)
    end
    return size
end

-- 分割字符串
function string.split(input, delimiter)
    input = tostring(input)
    delimiter = tostring(delimiter)
    if (delimiter=='') then return false end
    local pos,arr = 0, {}
    -- for each divider found
    for st,sp in function() return string.find(input, delimiter, pos, true) end do
        table.insert(arr, string.sub(input, pos, st - 1))
        pos = sp + 1
    end
    table.insert(arr, string.sub(input, pos))
    return arr
end

--get filename
function getFileName(str)
    local idx = str:match(".+()%.%w+$")
    if(idx) then
        return str:sub(1, idx-1)
    else
        return str
    end
end

--get file postfix
function getExtension(str)
    return str:match(".+%.(%w+)$")
end


local function decode_uuid(t)
	if #t ~= 22 then
		return t
	end

	local i = { 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 }
	local a = { "", "", "", "", "", "", "", "", "-", "", "", "", "", "-", "", "", "", "", "-", "", "", "", "", "-", "", "", "", "", "", "", "", "", "", "" }
	local n = { [0] = '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }
	local s = { 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18, 20, 21, 22, 23, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 }

	a[1] = t:sub(1, 1)
	a[2] = t:sub(2, 2)
	local r = 3

	for e = 2, 20, 2 do
		local o = i[string.byte(t, e + 1)+ 1]
		local l = (i[string.byte(t, e + 2) + 1])
		a[s[r]] = n[bit.rshift(o, 2)]
		a[s[r + 1]] = n[bit.bor(bit.lshift(bit.band(o, 3), 2), bit.rshift(l, 4))]
		a[s[r + 2]] = n[bit.band(15, l)]
		--print(o, l, a[s[r]], a[s[r + 1]], a[s[r + 2]])
		r = r + 3
	end
	-- print(dump(a))
	return table.concat(a)
end



-- 获取文件扩展名 音效文件 只管在Native里的  import里的 不用管,  png和jpg的扩展名可能会不对,
function getFileExtension(str, flag)
	if str == "cc.AnimationClip" then
		return ".anim"
	--elseif str == "cc.Texture2D" then
	--	return ".png"
	elseif str == "cc.SpriteAtlas" then
		return ".plist"
	--elseif str == "cc.SpriteFrame" then
	--	if flag then
	--		return ".json"
	--	else
	--		return ".png"
	--	end
	elseif str == "cc.Prefab" then
		return ".prefab"
	elseif str == "cc.BitmapFont" then
		return ".fnt"
	elseif str == "sp.SkeletonData" then
		return ".json"
	elseif str == "cc.Asset" then
		return ".atlas"
	--elseif str == "cc.AudioClip" then
	--	return ".mp3"
	--else
	--	return ".unknown"
	end
end

-- 把目录 拆分成 目录和文件名
function getFullFileName(str)
	local tbl = string.split(str,"/");
	if #tbl > 1 then
		local filename = tbl[#tbl];
		tbl[#tbl] = nil;
		return  table.concat(tbl, "\\\\"), filename
	else
		return "", tbl[1]
	end
end

-- 读取JSON文件
function readJsonFile(file)
	if not io.exists(file) then
		print( file .. " is not exists")
		return
	end

	local content = io.readfile(file)
	local decode = json.decode.decode
	--local opt = (...)
	local strict = opt and opt:match('--strict')
	return decode(content, strict)
end

-- 复制文件
function copy_file(source_path, destination_path)
    local source_file = io.open(source_path, "rb") -- 打开源文件进行二进制读取
    if not source_file then
        print("无法打开源文件")
        return false
    end

    local content = source_file:read("*a") -- 读取整个文件内容
    source_file:close() -- 关闭源文件

    local destination_file = io.open(destination_path, "wb") -- 打开目标文件进行二进制写入
    if not destination_file then
        print("无法创建目标文件")
        return false
    end

    destination_file:write(content) -- 写入内容
    destination_file:close() -- 关闭目标文件

	print("复制文件:", source_path, destination_path, "\t成功")
    return true
end

-- 列出所有文件缓到指定表里
function cacheAllFiles(rootpath, cache)
	for entry in lfs.dir(rootpath) do
		if entry ~= '.' and entry ~= '..' then
			local path = rootpath .. '\\' .. entry
			local attr = lfs.attributes(path)
			--print(path)
			local filename = getFileName(entry)
			local names = string.split(filename, ".")

			if attr.mode ~= 'directory' then
				local postfix = getExtension(entry)
				table.insert(cache, {path = path, filename = names[1], ext = "." .. postfix});
				--print(path ..'\t'..names[1] .. '\t' .. postfix)
			else
				--print(filename .. '\t' .. attr.mode)
				cacheAllFiles(path, cache)
			end
		end
	end
end

function checkFile(uuid, tbl)
	for k, v in pairs(tbl) do
		if v.filename == uuid then
			return v
		end
	end
end

-- plist子图内容
function makePlistItem(json_obj)
	local plistItem = [[
			<key>%s.png</key>
				<dict>
					<key>frame</key>
					<string>{{%d, %d},{%d,%d}}</string>
					<key>offset</key>
					<string>{%d,%d}</string>
					<key>rotated</key>
					<%s />
					<key>sourceSize</key>
					<string>{%d,%d}</string>
				</dict>
	]]
	local rotated = "false"
	if json_obj.rotated then
		rotated = "true"
	end

	return string.format(plistItem, json_obj.name, json_obj.rect[1], json_obj.rect[2], json_obj.rect[3], json_obj.rect[4], json_obj.offset[1], json_obj.offset[2], rotated, json_obj.originalSize[1], json_obj.originalSize[2])
end

function makePlistFile(plist_filename, packs, uuid, json_obj, filename)
	local plistItems = ""
	for k, v in pairs(json_obj._spriteFrames) do
		local uuid = v.__uuid__
		for k2, v2 in pairs(packs) do
			local idx = 0
			for k1,v1 in ipairs(v2) do
				if uuid == v1 then
					idx = k1;
					break;
				end
			end
			if idx ~= 0 then
				-- 在k合集包里  plist文件 要特殊处理,
				local pack = checkFile(k2, nativeCache)
				if not pack then
					pack = checkFile(k2, importCache)
				end

				if pack then
					local json_obj1 = readJsonFile(pack.path)
					--print(json.encode(json_obj1[idx]))
					local item = makePlistItem(json_obj1[idx].content)
					plistItems = plistItems .. item
				end
			end
		end
	end
	--local plistItems = makePlistItem(item_name, size, offset, rotated)
	return writePlistFile(plist_filename, filename, atlas_size, plistItems)
end

-- 写入plist文件
function writePlistFile(plist_filename, atlas_filename, atlas_size, plistItems)
	local plistModel = [[
	<?xml version="1.0" encoding="utf-8"?>
	<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
	<plist version="1.0">
		<dict>
			<key>frames</key>
			<dict>
	%s
			</dict>
			<key>metadata</key>
			<dict>
				<key>format</key>
				<integer>2</integer>
				<key>textureFileName</key>
				<string>%s.png</string>
				<key>realTextureFileName</key>
				<string>%s.png</string>
				<key>size</key>
				<string>{%d,%d}</string>
			</dict>
			<key>texture</key>
			<dict>
				<key>width</key>
				<integer>%d</integer>
				<key>height</key>
				<integer>%d</integer>
			</dict>
		</dict>
	</plist>
	]]

	--make_directory_recursive(outputs..path)
	local content = string.format(plistModel, plistItems, atlas_filename, atlas_filename, 0, 0, 0, 0)
	--print(content)
	local rt = io.writefile(plist_filename, content)
	if rt then
		print("写入文件:", plist_filename, "\t成功")
	end
	return rt
end

function makeFntFile(fnt_filename, json_obj, filename)
	local fntModel = [[
info face="%s" size=%d bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1
common lineHeight=%d base=26 scaleW=512 scaleH=256 pages=1 packed=0 alphaChnl=1 redChnl=0 greenChnl=0 blueChnl=0
page id=0 file="%s"
chars count=%d
%s
kernings count=0
]]
	local fntItem = [[char id=%d x=%d y=%d width=%d height=%d xoffset=%d yoffset=%d xadvance=%d page=0 chnl=0 letter=""
]]
	local fntItems = ""
	local count = 0;
	for k, v in pairs(json_obj._fntConfig.fontDefDictionary) do
		local item = string.format(fntItem, k, v.rect.x, v.rect.y, v.rect.width, v.rect.height, v.xOffset, v.yOffset, v.xAdvance)
		fntItems = fntItems .. item
		count = count + 1;
	end

	local content = string.format(fntModel, json_obj._name, json_obj.fontSize, json_obj._fntConfig.commonHeight, json_obj._fntConfig.atlasName, count, fntItems)
	print(content)
	local rt = io.writefile(fnt_filename, content)
	if rt then
		print("写入文件:", fnt_filename, "\t成功")
	end
	return rt
end

function main(game_path, config_file)
	local path = game_path -- 游戏资源路径
	lfs.chdir(path)


	local config = readJsonFile(config_file)	-- 读取config.4a6d4.json
	if not config then
		print("config is nil")
		return
	end

	-- 先把 import和native目录的文件列表全缓存起来,方便后面查找
	cacheAllFiles(path.."\\import", importCache)
	cacheAllFiles(path.."\\native", nativeCache)


	-- 生成指定目录 如果父目录不存也一起创建
	local outputs = "outputs\\"
	make_directory_recursive(outputs)


	for key, value in pairs(config.paths) do
		local path, filename = getFullFileName(value[1])
		local extentsion = getFileExtension(value[2], value[3])
		local uuid = decode_uuid(key)
		print(key, uuid, value[1], path, filename..(extentsion or ".unknown"))

		if not value[3] then
			local item = checkFile(uuid, nativeCache)
			if item then
				--  在native里找到了对应的文件,把文件复制到缓存目录里
				make_directory_recursive(outputs..path)
				local outfile = outputs..path.."\\"..filename..(extentsion or item.ext)
				if value[2] == "cc.SpriteAtlas" then
					local content = readJsonFile(item.path)
					makePlistFile(outfile, config.packs, key, content, filename)
				elseif value[2] == "sp.SkeletonData" then
					local json_obj = readJsonFile(item.path)
					io.writefile(outfile, json.encode(json_obj._skeletonJson))
				elseif value[2] == "cc.BitmapFont" then
					local json_obj = readJsonFile(item.path)
					makeFntFile(outfile, json_obj, filename)
				else
					copy_file(item.path, outfile)
				end
			else
				item = checkFile(uuid, importCache)
				if item then
					--  在import里找到了对应的文件,把文件复制到缓存目录里
					make_directory_recursive(outputs..path)
					local outfile = outputs..path.."\\"..filename..(extentsion or item.ext)
					if value[2] == "cc.SpriteAtlas" then
						local content = readJsonFile(item.path)
						makePlistFile(outfile, config.packs, key, content, filename)
					elseif value[2] == "sp.SkeletonData" then
						local json_obj = readJsonFile(item.path)
						io.writefile(outfile, json.encode(json_obj._skeletonJson))
					elseif value[2] == "cc.BitmapFont" then
						local json_obj = readJsonFile(item.path)
						makeFntFile(outfile, json_obj, filename)
					else
						copy_file(item.path, outfile)
					end
				else
					-- 在import 和 native 里都找不到, 这个文件可能合并在某一个文件里
					for k, v in pairs(config.packs) do
						local idx = 0;
						for k1,v1 in ipairs(v) do
							if key == v1 then
								idx = k1;
								break;
							end
						end
						if idx ~= 0 then
							-- 在k合集包里  plist文件 要特殊处理,
							local pack = checkFile(k, nativeCache)
							if not pack then
								pack = checkFile(k, importCache)
							end

							if pack then
								local json_obj = readJsonFile(pack.path)
								local content = json_obj[idx];
								make_directory_recursive(outputs..path)
								local outfile = outputs..path.."\\"..filename..(extentsion or pack.ext)
								local rt = false
								if value[2] == "cc.SpriteAtlas" then
									rt = makePlistFile(outfile, config.packs, key, content, filename)
								elseif value[2] == "sp.SkeletonData" then
									rt = io.writefile(outfile, json.encode(content._skeletonJson))
								elseif value[2] == "cc.BitmapFont" then
									rt = makeFntFile(outfile, content, filename)
								else
									rt = io.writefile(outfile, json.encode(content))
								end
								if rt then
									print("写入文件:", outfile, "\t成功")
									break;
								end
							end
						end
					end
				end
			end
		end
	end

	-- 找出来 场景的fire文件
	local scenes = config.scenes;
	for key, value in pairs(scenes) do
		for k, v in pairs(config.packs) do
			local idx = 0;
			for k1,v1 in ipairs(v) do
				if value == v1 then
					idx = k1;
					break;
				end
			end
			if idx ~= 0 then
				-- 在k合集包里
				local pack = checkFile(k, nativeCache)
				if not pack then
					pack = checkFile(k, importCache)
				end

				if pack then
					local json_obj = readJsonFile(pack.path)
					local content = json_obj[idx];
					local filenames = string.split(key, "/");
					local filename = filenames[#filenames];
					local outfile = outputs.."\\"..filename
					local rt = io.writefile(outfile, json.encode(content))
					if rt then
						print("写入文件:", outfile, "\t成功")
						break;
					end
				end
			end
		end
	end


end

local game_path = "D:\\BaiduNetdiskDownload\\assets\\lolliPopGame"
local config_file = "config.4a6d4.json"
main(game_path, config_file)
